using System;
using System.Collections.Generic;
using System.Text;
using Xyne.Templates;
using GarageGames.Torque.Core;
using Xyne.Components;
using GarageGames.Torque.Sim;
using Microsoft.Xna.Framework.Input;
using GarageGames.Torque.Platform;
using Microsoft.Xna.Framework;
using GarageGames.Torque.Materials;
using GarageGames.Torque.T2D;
using Xyne.Templates.PuzzlePieceTemplate;
using Xyne.Utilities;
using Xyne.Events;
using Xyne.Objects;
using GarageGames.Torque.Lighting;
using Xyne.Templates.PuzzleGlowTemplate;

namespace Xyne.Regions
{
    public class PuzzleRegionImpl : PuzzleRegion
    {
        // The board dimensions
        public int boardWidth;
        public int boardHeight;
        // Unload callback
        private UnloadPatternDelegate onUnload;
        // Current player pieces
        private PlayerPieces playerPieces;
        // Sweep animation
        private Sweep sweep;
        // The loaded pattern
        private PatternValue[,] pattern = null;
        // A temporary list of pieces
        private List<PuzzlePiece> tempPieces;
        // A list of pattern matches
        private List<List<PuzzlePiece>> matches;
        // The X position of newly created gems
        private int startX;
        // The number of patterns selected
        private int numSelected;

        public PuzzleRegionImpl(Rectangle Bounds, QueueRegion queueRegion) : base(Bounds, queueRegion)
        {
            startX = Bounds.Left + ((Bounds.Width - (Bounds.Width % Game.PuzzlePieceWidth)) / 2);
            boardWidth = (Bounds.Width - (Bounds.Width % Game.PuzzlePieceWidth)) / Game.PuzzlePieceWidth;
            boardHeight = (Bounds.Height - (Bounds.Height % Game.PuzzlePieceHeight)) / Game.PuzzlePieceHeight;
            tempPieces = new List<PuzzlePiece>();
            matches = new List<List<PuzzlePiece>>();
            numSelected = 0;

            // Temporary background
            T2DStaticSprite background = new T2DStaticSprite();
            background.Size = new Vector2(Bounds.Width, Bounds.Height);
            background.Position = new Vector2(Bounds.Left + (Bounds.Width / 2), Bounds.Top + (Bounds.Height / 2));
            background.Layer = Game.BackgroundLayer;
            SimpleMaterial material = new SimpleMaterial();
            material.TextureFilename = @"data\images\ui\puzzleboard\puzzleregion.png";
            background.Material = material;
            TorqueObjectDatabase.Instance.Register(background);
            
            sweep = (Sweep)Sweep.Template.Clone();
            sweep.WorldLimit.MoveLimitMin = new Vector2(Bounds.Left, Bounds.Top);
            sweep.WorldLimit.MoveLimitMax = new Vector2(Bounds.Right, Bounds.Bottom);
            sweep.Size = new Vector2(40, Bounds.Height);
            sweep.OnSweepDone = new Sweep.SweepDoneDelegate(OnSweepDone);
            sweep.CheckForSelection = new Sweep.CheckForSelectionDelegate(CheckSelection);
            TorqueObjectDatabase.Instance.Register(sweep);

            // For testing deletion
            InputMap inputMap = PlayerManager.Instance.GetPlayer(0).InputMap;
            int keyboardId = InputManager.Instance.FindDevice("keyboard");
            if (keyboardId >= 0)
            {
                inputMap.BindAction(keyboardId, (int)Keys.Delete, OnDelete);
            }

            sweep.ResetSweepState();
        }

        public override bool LoadPattern(PatternValue[,] pattern, UnloadPatternDelegate onUnload)
        {
            if (this.pattern != null)
                return false;

            this.onUnload = onUnload;

            if (CheckPattern(pattern))
            {
                this.pattern = pattern;
                sweep.ResetSweepState();
                sweep.StartSweep();

                return true;
            }

            return false;
        }

        public override int UnloadPattern()
        {
            int numPatterns = numSelected;
            numSelected = 0;

            sweep.StopSweep();

            this.pattern = null;

            List<PuzzlePiece> pieces = new List<PuzzlePiece>();
            TorqueObjectDatabase.Instance.FindRegisteredObjects<PuzzlePiece>(ref pieces);

            foreach (PuzzlePiece piece in pieces)
            {
                if (piece.Selected)
                {
                    TorqueObjectDatabase.Instance.Unregister(piece);

                    foreach (PuzzlePiece above in pieces)
                    {
                        if (above.IsRegistered && above.Position.Y < piece.Position.Y &&
                            Math.Round(above.Position.X) == Math.Round(piece.Position.X))
                        {
                            above.Physics.VelocityY = Game.FallingVelocity;
                        }
                    }

                    continue;
                }

                if (piece.Material is LightingMaterial && piece.InPlay)
                {
                    LightingMaterial material = piece.Material as LightingMaterial;
                    material.Opacity = 1.0f;
                }

                piece.Selectable = false;
                piece.Selected = false;
            }

            return numPatterns;
        }

        private PuzzlePiece[,] GetBoardState()
        {
            PuzzlePiece[,] board = new PuzzlePiece[boardWidth, boardHeight];
            List<PuzzlePiece> pieces = TorqueObjectDatabase.Instance.FindRegisteredObjects<PuzzlePiece>();
            foreach (PuzzlePiece piece in pieces)
            {
                if (piece.Material is LightingMaterial && piece.InPlay)
                {
                    LightingMaterial material = piece.Material as LightingMaterial;
                    material.Opacity = 0.5f;
                }

                piece.Selectable = false;

                if (piece.MountedTo == null && piece.InPlay)
                {
                    board[(int)((Math.Round(piece.Position.X) - 20 - Bounds.Left) / 40), (int)((Math.Round(piece.Position.Y) - 20 - Bounds.Top) / 40)] = piece;
                }
            }

            return board;
        }

        private void CheckSelection(PuzzlePiece piece)
        {
            if (!piece.Selected)
            {
                // TODO : optimize
                foreach (List<PuzzlePiece> list in matches)
                {
                    bool contains = false;
                    bool valid = true;

                    foreach (PuzzlePiece tempPiece in list)
                    {
                        if (tempPiece == piece)
                            contains = true;
                        if (Math.Round(piece.Position.X) < Math.Round(tempPiece.Position.X))
                            valid = false;
                    }

                    if (contains && valid)
                    {
                        foreach (PuzzlePiece tempPiece in list)
                        {
                            T2DStaticSprite sprite = PuzzleGlow.GetNewPuzzleGlow();
                            sprite.WarpToPosition(tempPiece.Position, 0);
                            TorqueObjectDatabase.Instance.Register(sprite);

                            PuzzlePulse pulse = (PuzzlePulse)PuzzlePulse.Template.Clone();
                            pulse.Visible = true;
                            pulse.WarpToPosition(tempPiece.Position, 0);
                            TorqueObjectDatabase.Instance.Register(pulse);

                            tempPiece.Selected = true;
                        }

                        numSelected++;

                        PatternSelectedEvent e = new PatternSelectedEvent(this);
                        e.NumSelected = numSelected;

                        Game.Network.HandleEvent(e);

                        if (numSelected == Game.MaxCardLevel)
                        {
                            OnSweepDone();
                        }

                        return;
                    }
                }
            }
        }

        private bool CheckPattern(PatternValue[,] pattern)
        {
            matches.Clear();

            int minX = -1, minY = -1, anchorY = -1;
            for (int x = 0; x < pattern.GetLength(0); x++)
                for (int y = 0; y < pattern.GetLength(1); y++)
                {
                    if (minX == -1 && pattern[x, y].Value != PatternValueEnum.None)
                    {
                        minX = x;
                        anchorY = y;
                    }
                    if (minY == -1 && pattern[x, y].Value != PatternValueEnum.None)
                    {
                        minY = y;
                    }
                }

            if (minX == -1 || anchorY == -1)
                return false;

            PuzzlePiece[,] board = GetBoardState();

            // Holy non-existent christ, it gets hairy here!
            bool success = false;
            for (int x = 0; x < boardWidth; x++)
                for (int y = 0; y < boardHeight; y++)
                {
                    // if the first part of the pattern matches
                    if (board[x, y] != null && PatternValue.Matches(pattern[minX, anchorY].Value, board[x, y].ID))
                    {
                        // check the rest of the pattern
                        tempPieces.Clear();
                        bool match = true;
                        for (int patternX = minX; patternX < pattern.GetLength(0) && match; patternX++)
                            for (int patternY = minY; patternY < pattern.GetLength(1) && match; patternY++)
                            {
                                if (match && pattern[patternX, patternY].Value != PatternValueEnum.None)
                                {
                                    if (x + patternX - minX < 0 || x + patternX - minX >= boardWidth ||
                                        y + patternY - minY < 0 || y + patternY - minY >= boardHeight)
                                        match = false;
                                    else if (board[x + patternX - minX, y + patternY - minY] == null)
                                        match = false;
                                    else
                                    {
                                        if (PatternValue.Matches(pattern[patternX, patternY].Value, board[x + patternX - minX, y + patternY - minY].ID))
                                        {
                                            tempPieces.Add(board[x + patternX - minX, y + patternY - minY]);
                                        }
                                        else
                                        {
                                            match = false;
                                        }
                                    }
                                }
                            }

                        if (match)
                        {
                            // add to the list of matches
                            matches.Add(new List<PuzzlePiece>(tempPieces));

                            foreach (PuzzlePiece piece in tempPieces)
                            {
                                piece.Selectable = true;

                                if (piece.Material is LightingMaterial && piece.InPlay)
                                {
                                    LightingMaterial material = piece.Material as LightingMaterial;
                                    material.Opacity = 1.0f;
                                }
                            }

                            success = true;
                        }
                    }
                }

            return success;
        }

        public override void Start()
        {
            
            GetNewGem();
        }

        private void GetNewGem()
        {
            playerPieces = QueueRegion.GetNextPiece();
            playerPieces.PlayerPiecesDone = new PlayerPieces.PlayerPiecesDoneCallback(PlayerPiecesDone);
            BindMoveToPiece(playerPieces.Components.FindComponent<PuzzleMove>());
            playerPieces.WarpToPosition(new Vector2(startX, Bounds.Top + (Game.PuzzlePieceHeight / 2)), 0);
            playerPieces.CollisionsEnabled = true;
            playerPieces.First.InPlay = true;
            playerPieces.Second.InPlay = true;
        }

        private void BindMoveToPiece(PuzzleMove move)
        {
            InputMap inputMap = PlayerManager.Instance.GetPlayer(0).InputMap;
             
            int keyboardId = InputManager.Instance.FindDevice("keyboard");
            if (keyboardId >= 0)
            {
                inputMap.BindAction(keyboardId, (int)Keys.Right, move.OnRight);
                inputMap.BindAction(keyboardId, (int)Keys.Left, move.OnLeft);
                inputMap.BindAction(keyboardId, (int)Keys.Down, move.OnDown);
                inputMap.BindAction(keyboardId, (int)Keys.Up, move.OnRotate);
            }
            int gamepadId = InputManager.Instance.FindDevice("gamepad0");
            if (gamepadId >= 0)
            {
                inputMap.BindAction(gamepadId, (int)XGamePadDevice.GamePadObjects.LeftThumbRightButton, move.OnRight);
                inputMap.BindAction(gamepadId, (int)XGamePadDevice.GamePadObjects.LeftThumbLeftButton, move.OnLeft);
                inputMap.BindAction(gamepadId, (int)XGamePadDevice.GamePadObjects.LeftThumbDownButton, move.OnDown);
                inputMap.BindAction(gamepadId, (int)XGamePadDevice.GamePadObjects.RightShoulder, move.OnRotate);
                inputMap.BindAction(gamepadId, (int)XGamePadDevice.GamePadObjects.LeftShoulder, move.OnReverseRotate);
            }
        }

        private void OnSweepDone()
        {
            int numGems = UnloadPattern();

            if (onUnload != null)
                onUnload(numGems);
        }

        private void RemoveGem(PuzzlePiece delete)
        {
            List<PuzzlePiece> pieces = new List<PuzzlePiece>();
            TorqueObjectDatabase.Instance.FindRegisteredObjects<PuzzlePiece>(ref pieces);
            
            foreach (PuzzlePiece piece in pieces)
            {
                if (piece.Position.X == delete.Position.X && piece.Position.Y < delete.Position.Y)
                {
                    piece.Physics.VelocityY = 100;
                }
            }

            TorqueObjectDatabase.Instance.Unregister(delete);
        }

        private void PlayerPiecesDone(PlayerPieces player)
        {
            List<PuzzlePiece> pieces = new List<PuzzlePiece>();
            TorqueObjectDatabase.Instance.FindRegisteredObjects<PuzzlePiece>(ref pieces);

            foreach (PuzzlePiece piece in pieces)
            {
                if (piece.MountedTo == player)
                {
                    bool falling = piece.Position.Y + (piece.Size.Y / 2) < piece.WorldLimit.MoveLimitMax.Y;
                    if (falling)
                    {
                        foreach (PuzzlePiece below in pieces)
                        {
                            if (piece.Position.X == below.Position.X && piece.Position.Y + piece.Size.Y == below.Position.Y)
                            {
                                falling = false;
                            }
                        }
                    }

                    if (falling)
                    {
                        piece.Physics.VelocityY = Game.FallingVelocity;
                    }
                    else
                    {
                        piece.Physics.VelocityY = 0;
                    }

                    piece.CollisionsEnabled = true;
                    piece.Mount(null, "", false);
                }
            }

            Vector2 playerPosition = player.Position;
            player.PlayerPiecesDone = null;
            player.MarkForDelete = true;

            bool spawn = true;
            foreach (PuzzlePiece piece in pieces)
            {
                piece.ObjectType += TorqueObjectDatabase.Instance.GetObjectType("LandedPiece");
                if ((int)((Math.Round(piece.Position.Y) - 20 - Bounds.Top) / 40) == 0 &&
                    ((int)(Math.Round(piece.Position.X)) + 20 == startX ||
                    ((int)(Math.Round(piece.Position.X)) - 20 == startX)))
                {
                    if (Game.Network != null)
                    {
                        Game.Network.HandleEvent(new GameLostEvent(this, GameLostEvent.PuzzleBoardFilled));
                    }
                }
            }

            if (spawn)
                GetNewGem();
        }

        private void OnDelete(float val)
        {
            if (val > 0)
            {
                // For testing
                if (Game.Network != null)
                {
                    Game.Network.HandleEvent(new GameLostEvent(this, GameLostEvent.PuzzleBoardFilled));
                }
            }
        }
       
    }
}
